# -*- coding: utf-8 -*-

# Copyright 2009 David Lee Miller
#
# This file is part of POSSM (Python Open Source Scanning Microscopy).
#
# POSSM is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# POSSM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License         
# along with POSSM.  If not, see <http://www.gnu.org/licenses/>.
#                                                                           
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

from controller import n,Queue,time,cPickle,pexpect,ws
#from coarsemove import CoarseMove
from inchworm import Approach
import storedata as sd
from copy import copy

class LineInfo(object):
	'A container to send line data in.'
	DATATYPE = 'line'
	GRIDX = None
	GRIDY = None
	CHECKER = None
	ZSCALE = None
	ZOUT = None
	ZHV = None
	UO = None
	def __init__(self,data,lineN,shape,scan,scantype,extent,close=False):
		self.LINE = lineN
		self.SCAN = scan
		self.SHAPE = shape
		self.data = data
		self.close = close
		self.EXTENT = extent
		self.TYPE = scantype

class Recovery(object):
	'''
	Used to store data in case of crash.
	'''
	def __init__(self,xoff,yoff,tip):
		self.xoff = xoff
		self.yoff = yoff
		self.tip = tip

class PointOperations(object):
	'''
	A Set of functions specific to point level operations. 
	'''
	FORWARD = 1
	REVERSE = -1
	ANG_PER_VOLT_X = ws.ANG_PER_VOLT_X
	ANG_PER_VOLT_Y = ws.ANG_PER_VOLT_Y
	ANG_PER_VOLT_Z = ws.ANG_PER_VOLT_Z
	VOLTS_PER_ADU = ws.VOLTS_PER_ADU
	SCAN_GAIN = ws.SCAN_GAIN
	READ_TIME = ws.READ_TIME
	
	Z_MULTIPLIER = ws.Z_MULTIPLIER
	
	def toPixel(self,pix_int):
		'''
		Assumes 16-bit D/A. Takes the pixel size in dac steps as given from
		the client and uses it to calculate what pixel a specific integer will
		fall under.
		
		Keyword Arguments:
		pix_int -- the dac value from which a pixel number is calculated
		
		'''
		
		pix_width = self.package.DAC_AVGS[0]*self.package.DAC_STEP[0]
		pix_num = n.divide(pix_int - (65536/2) + int((self.package.SCAN_X[0]*pix_width)/2),pix_width)
		return pix_num
	
	def fromPixel(self,pix_num):
		'''
		Assumes 16-bit D/A. Takes a pixel number and returns the dac
		step represented by that pixel.
		
		Keyword Arguments:
		pix_num -- the pixel number from which a dac integer is calculated.
		
		'''
		
		pix_width = self.package.DAC_AVGS[0]*self.package.DAC_STEP[0]
		int_location = n.add(pix_num*pix_width,(65536/2) - int((self.package.SCAN_X[0]*pix_width)/2))
		return int_location
	
	def angstromsToVolts(self,angs,outputid):
		'''
		Converts angstroms (in piezo movement) to a voltage for the selected output. Uses gains and
		piezo values from the worldsettings file to make the conversion. The voltage
		values can be used for output on the analog output board.
		
		Keyword Arguments:
		angs -- the value in angstroms to be converted into volts
		outputid -- the specifier for which conversion will be done
		
		'''
		conv = None
		if outputid == 'x offset': conv = 1/(self.ANG_PER_VOLT_X)
		elif outputid == 'y offset': conv = 1/(self.ANG_PER_VOLT_Y)
		
		if conv == None: raise 'Invalid Conversion'
		
		return angs*conv
	
	def voltsToAngstroms(self,vlts,outputid):
		'''
		Converts angstroms (in piezo movement) to a voltage for the selected output. Uses gains and
		piezo values from the worldsettings file to make the conversion. The voltage
		values can be used for output on the analog output board.
		
		Keyword Arguments:
		angs -- the value in angstroms to be converted into volts
		outputid -- the specifier for which conversion will be done
		
		'''
		conv = None
		if outputid == 'x offset': conv = (self.ANG_PER_VOLT_X)
		elif outputid == 'y offset': conv = (self.ANG_PER_VOLT_Y)
		
		if conv == None: raise 'Invalid Conversion'
		
		return vlts*conv
	
	def physicalImage(self,xyunit='Angstroms'):
		'''
		Returns the physical size of the image. The unit default is Angstroms, but can
		be changed to other metric units. The name of the desired unit should be the full name
		and capitalized (eg. 'Nanometers').
		'''
		dac_avgs = self.package.DAC_AVGS[0]
		dac_step = self.package.DAC_STEP[0]
		scan_x = self.package.SCAN_X[0]
		scan_y = self.package.SCAN_Y[0]
		phys_x,phys_y=self.package.UO._ConvertPixels(scan_x,scan_y,dac_step*dac_avgs,self.package.UO.units[xyunit][0],self.package.GAIN_XHV[0],self.package.GAIN_YHV[0])
		self.package.PHYS_X[0] = phys_x # Adds the calculated physical units to the package
		self.package.PHYS_Y[0] = phys_y
		return phys_x,phys_y

	#def loopEdge(self,direction):
		#'''
		#Moves the piezos in a parabolic motion around the edge of a scan in order to
		#reduce accelerations that cause distortion. This function can keep the sides of
		#the image from appearing stretched or wavy.
		
		#Keyword Arguments:
		#direction -- the direction to accelerate/decellerate the piezos
		
		#'''
		##readtime = (self.package.N_READS[0]*self.READ_TIME)/4.e-6+self.package.POINT_DELAY[0]
		#if self.package.ACCEL_WIDTH[0] and direction == 1: #ramp down from a forward scan
			#for i in range(self.package.ACCEL_WIDTH[0]):
				#self.xmove_zpos.writeSingle(self.xmove_zpos.current_dac + 1,rate=self.XMAX_RATE)
				#self.xmove_zpos.microdelay(i*self.package.ACCEL_CURVE[0])
				##self.xmove_zpos.readFew(self.package.N_READS[0])
		#elif self.package.ACCEL_WIDTH[0] and direction == 2: #ramp down from a reverse scan
			#rev = range(self.package.ACCEL_WIDTH[0])
			#rev.reverse()
			#for i in rev:
				#self.xmove_zpos.writeSingle(self.xmove_zpos.current_dac - 1,rate=self.XMAX_RATE)
				#self.xmove_zpos.microdelay(i*self.package.ACCEL_CURVE[0])
				##self.xmove_zpos.readFew(self.package.N_READS[0])
		#elif self.package.ACCEL_WIDTH[0] and direction == -1: #ramp up for the forward scan
			#for i in range(self.package.ACCEL_WIDTH[0]):
				#self.xmove_zpos.writeSingle(self.xmove_zpos.current_dac + 1,rate=self.XMAX_RATE)
				#self.xmove_zpos.microdelay(i*self.package.ACCEL_CURVE[0])
				##self.xmove_zpos.readFew(self.package.N_READS[0])
		#elif self.package.ACCEL_WIDTH[0] and direction == -2: #ramp up for the reverse scan
			#rev = range(self.package.ACCEL_WIDTH[0])
			#rev.reverse()
			#for i in rev:
				#self.xmove_zpos.writeSingle(self.xmove_zpos.current_dac - 1,rate=self.XMAX_RATE)
				#self.xmove_zpos.microdelay(i*self.package.ACCEL_CURVE[0])
				##self.xmove_zpos.readFew(self.package.N_READS[0])
		#else: pass
	
	def _imageLineWSpec(self):
		'''
		Images the entire line. Can be used for any type of scan, but the _imageLineNoIV
		function is more efficient for scans that do not require iv's. This function starts by
		finding the integer locations of any iv's that need to be taken. Next it proceeds to
		take the iv's at the selected locations on the forward scan. Then the function takes
		the reverse scan without any stops and adds the data to the Z array.
		
		
		'''
	
		nsteps = self.package.SCAN_X[0]*self.package.DAC_AVGS[0]
		pos = self.fromPixel(0)
		line_spec = self.fromPixel(self.SPEC_MASK[self.yloc,:].nonzero()[0]) #get specs for the line from spec mask
		sleep_wave = copy(self.sleep_wave) #make an object copy of the sleep wave
		if pos < 0: pos = 0
		steps_taken = 0
		data_f = []
		data_r = []
		#self.loopEdge(-1)
		### The start of the forward scan. Spectroscopy taken at selected spots. ###
		if line_spec.any(): #check if any spectroscopy is on the current line
			for i in line_spec:
				split_nsteps = self.toPixel(i)\
				*self.package.DAC_AVGS[0]-self.toPixel(pos)*self.package.DAC_AVGS[0]
				if split_nsteps > 0:
					data_f += self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,split_nsteps,sleep_wave[0],self.FORWARD,\
					self.package.DAC_AVGS[0],self.package.DAC_STEP[0],period=self.XMAX_PERIOD)
				steps_taken += split_nsteps
				pos = i
				if self.package.TYPE == 'imagewiv':
					self._doIV(self.yloc,len(data_f))
				elif self.package.TYPE == 'imagewiz':
					self._doIZ(self.yloc,len(data_f))
				elif self.package.TYPE == 'imagewzv':
					self._doZV(self.yloc,len(data_f))
			if steps_taken < nsteps:
				final_steps = (nsteps-steps_taken)
				data_f += self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,final_steps,sleep_wave[0],self.FORWARD,\
				self.package.DAC_AVGS[0],self.package.DAC_STEP[0],\
				period=self.XMAX_PERIOD)
		else: #if no iv's, image the line without stopping
			data_f = self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,nsteps,sleep_wave[0],self.FORWARD,\
			self.package.DAC_AVGS[0],self.package.DAC_STEP[0],period=self.XMAX_PERIOD)
			pos = self.fromPixel(len(data_f))
		#self.loopEdge(1)
		if self.package.LINE_DELAY[0]: self._lineDelay()
		sleep_wave = copy(self.sleep_wave)
		data_r = []
		#self.loopEdge(2)
		data_r = self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,nsteps,sleep_wave[0],self.REVERSE,\
		self.package.DAC_AVGS[0],self.package.DAC_STEP[0],period=self.XMAX_PERIOD)
		#self.loopEdge(-2)
		self.Z[0][0][self.yloc:self.yloc+1,:] = n.multiply(data_r,self.Z_MULTIPLIER)
		self.Z[0][1][self.yloc:self.yloc+1,:] = n.multiply(data_f,self.Z_MULTIPLIER)
		if self.package.LINE_DELAY[0]: self._lineDelay()
	
	def _imageLine(self,mbias):
		'''
		Images the entire line with no stops for iv's. Also the argument mbias is used
		for multibias scans. The argument is used only in specifying the location in the
		Z array where the data is to be put.
		
		Keyword Arguments:
		mbias -- the integer array location for the line of data
		
		'''
	
		nsteps = self.package.SCAN_X[0]*self.package.DAC_AVGS[0]
		sleep_wave = copy(self.sleep_wave)
		data_f = []
		data_f = self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,nsteps,sleep_wave[0],self.FORWARD,\
		self.package.DAC_AVGS[0],self.package.DAC_STEP[0],period=self.XMAX_PERIOD)
		if self.package.LINE_DELAY[0]: self._lineDelay()
		sleep_wave = copy(self.sleep_wave)
		data_r = []
		data_r = self.xmove_zpos.rwScan(ws.SERIAL_ACQ_Z,nsteps,sleep_wave[0],self.REVERSE,\
		self.package.DAC_AVGS[0],self.package.DAC_STEP[0],period=self.XMAX_PERIOD)
		self.Z[mbias][0][self.yloc:self.yloc+1,:] = n.multiply(data_r,self.Z_MULTIPLIER)
		self.Z[mbias][1][self.yloc:self.yloc+1,:] = n.multiply(data_f,self.Z_MULTIPLIER)
		if self.package.LINE_DELAY[0]: self._lineDelay()

	#def ivTip(self,dv):
		#'''
		#Changes the voltage on the tip by the appropriate dv. Used most often in
		#scans where tip bias is the dependent variable.
		
		#Keyword argument:
		#dv -- the amount to change the tip voltage by. takes - or + values.
		
		#'''
		#self._jumpTip(self.tip.currentPhysical()+dv)
		
	#def ivTip2(self,dv):
		#'''
		#Changes the voltage on the tip by the appropriate dv. Used most often in
		#scans where tip bias is the dependent variable. This version uses the safer,
		#_setTip function which has a threshold protect feature.
		
		#Keyword argument:
		#dv -- the amount to change the tip voltage by. takes - or + values.
		
		#'''
		#self._setTip(self.tip.currentPhysical()+dv,report=False)
	##def _doIV(self,yloc,xloc): # Depricated. New function given below.
		##'''
		##Scans the tip bias over a specified range and records the tunneling current. First, it determines
		##the voltage step size, then ramps to the starting voltage (always the same sign as the scanning bias)
		##and then does the scan. Various delays are strategically placed in the scan to as settling times for
		##the equipment. Arguments are used to provide the location of where the iv was taken.
		
		##Keyword Arguments:
		##yloc -- the current y position
		##xloc -- the current x position
		
		##'''
		##start_time = time.time()
		##self.IV[str((int(yloc),int(xloc)))] = []
		##dv = self.getdV(self.package.IV_MIN[0],self.package.IV_MAX[0],self.package.IV_STEPS[0])
		##if self.tip.currentPhysical() > 0: #Detects sign of starting bias
			##start = self.package.IV_MAX[0] #NOT TECHNICALLY RIGHT
			##dv *= -1
		##else:
			##start = self.package.IV_MIN[0]
		##self._holdTip() #flips the hold bit on
		##self.tip.microdelay(self.package.IV_SETDELAY[0])
		##for i in range(self.package.IV_MULTIZ[0]+1):
			##xlist_f = []
			##ylist_f = []
			##xlist_r = []
			##ylist_r = []
			##self._moveZPlane(self.package.IV_Z[i][0])
			##if i == 0: self._jumpTip(start)
			##self.tip.microdelay(self.package.IV_PREDELAY[0])
			##for i in range(self.package.IV_STEPS[0]):
				##xlist_f.append(self._convertBias(self.tip.currentPhysical()))
				##ylist_f.append(self.tip_current.readFew(self.package.IV_READS[0]))
				##self.ivTip(dv)
				##self.tip.microdelay(self.package.IV_DELAY[0])
			##self.dOut.microdelay(self.package.IV_MIDDELAY[0])
			##for i in range(self.package.IV_STEPS[0]):
				##self.ivTip(-dv)
				##self.tip.microdelay(self.package.IV_DELAY[0])
				##xlist_r.append(self._convertBias(self.tip.currentPhysical()))
				##ylist_r.append(self.tip_current.readFew(self.package.IV_READS[0]))
			##xlist_r.reverse()
			##ylist_r.reverse()
			##self.IV[str((int(yloc),int(xloc)))].append([[xlist_f,ylist_f],[xlist_r,ylist_r]])
		##self._moveZPlane(0)
		##self._jumpTip(self._convertBias(self.package.SAMPLE_BIAS[0]))
		##self._releaseHold()
		##self.tip.microdelay(self.package.IV_POSTDELAY[0])
		##self._outq.put('IV took %f seconds'%(time.time()-start_time))
	
	#def _doIV(self,yloc,xloc):
		#'''
		#Scans the tip bias over a specified range and records the tunneling current. First, it determines
		#the voltage step size, then ramps to the starting voltage (always the same sign as the scanning bias)
		#and then does the scan. Various delays are strategically placed in the scan to as settling times for
		#the equipment. Arguments are used to provide the location of where the iv was taken.
		
		#Keyword Arguments:
		#yloc -- the current y position
		#xloc -- the current x position
		
		#'''
		#self.IV[str((int(yloc),int(xloc)))] = []
		#nsteps = self.package.IV_STEPS[0]
		#ivstart = self.tip.device.PhysicalToInteger(self.tip.device.SUB_AO,self.tip.channel,self.tip.rng,self._convertBias(self.package.IV_START[0]))
		#ivend = self.tip.device.PhysicalToInteger(self.tip.device.SUB_AO,self.tip.channel,self.tip.rng,self._convertBias(self.package.IV_END[0]))
		#iv_io = self.io_tip
		#iv_io.current_dac = self.tip.current_dac
		#ramp_usec = (10**6)/self.package.RAMP_HZ[0]
		#if ivend<ivstart: ivdir = -1
		#else: ivdir = 1
		#step = int(round(abs(ivend-ivstart)/float(nsteps)))
		#delay = self.package.IV_DELAY[0]
		##if self.package.IV_RMPSTP[0] < step: ramp=self.package.IV_RMPSTP[0]
		##else: ramp=step
		#self._holdTip() #flips the hold bit on
		#self.tip.microdelay(self.package.IV_SETDELAY[0])
		#for i in range(self.package.IV_MULTIZ[0]+1):
			#xlist_f = []
			#ylist_f = []
			#xlist_r = []
			#ylist_r = []
			#self._moveZPlane(self.package.IV_Z[i][0])
			#if i == 0:
				##self._jumpTip(start)
				#self.tip.writeSingle(ivstart)
				#iv_io.current_dac = self.tip.current_dac
			#self.tip.microdelay(self.package.IV_PREDELAY[0])
			#ylist_f,xlist_f = iv_io.rwScan(nsteps,delay,ivdir,1,step,self.package.IV_READS[0],period=0,rmpstp=0,retw=True)
			#self.tip.microdelay(self.package.IV_MIDDELAY[0])
			#ylist_r,xlist_r = iv_io.rwScan(nsteps,delay,-1*ivdir,1,step,self.package.IV_READS[0],period=0,rmpstp=0,retw=True)
			#self.IV[str((int(yloc),int(xloc)))].append([[xlist_f,ylist_f],[xlist_r,ylist_r]])
		#self._moveZPlane(0)
		#self._jumpTip(self._convertBias(self.package.SAMPLE_BIAS[0]))
		#self._releaseHold()
		#self.tip.microdelay(self.package.IV_POSTDELAY[0])	
	
	def _doIV(self,yloc,xloc,setup=False):
		'''
		Scans the tip bias over a specified range and records the tunneling current. First, it determines
		the voltage step size, then ramps to the starting voltage (always the same sign as the scanning bias)
		and then does the scan. Various delays are strategically placed in the scan to as settling times for
		the equipment. Arguments are used to provide the location of where the iv was taken.
		
		Keyword Arguments:
		yloc -- the current y position
		xloc -- the current x position
		
		'''
		## Initialize the IV parameters
		self.IV[str((int(yloc),int(xloc)))] = []
		npts = self.package.IV_STEPS[0]
		vstart = self._convertBias(self.package.IV_START[0])
		vend = self._convertBias(self.package.IV_END[0])
		ivtime = self.package.IV_TIME[0]
		if setup == True: self.servo.SetupIV(npts,vstart,vend,ivtime)
		vstep = float(vend-vstart)/npts
		voltagesF = self._convertBias(n.arange(vstart,vend,vstep))
		voltagesR = self._convertBias(n.arange(vend,vstart,-vstep))

		Vtot = 0.0
		for i in range(self.package.IV_MULTIZ[0]+1):
			Vdist = self.package.IV_Z[i][0]/ws.ANG_PER_VOLT_Z
			Vchng = Vdist - Vtot
			Vtot += Vchng
			self.servo.ChangeZLG(Vchng)
			## Execute the IV
			data = self.servo.IV(npts,ivtime)
			ind = int(len(data))
			self.IV[str((int(yloc),int(xloc)))].append([[voltagesF,data[0:ind/2]],[voltagesR,data[ind/2:ind]]])
		self.servo.ChangeZLG(-Vtot) #set it back to original position
		self.servo.ResumeTunneling()

	#def _doIVLimit(self,yloc,xloc,lim_min,lim_max):
		#'''
		#Similar to _doIV, except this is a particular mode of IV where the bias is stopped from
		#scanning when the tunneling current goes above or below a specified range. The procedure is
		#to ramp toward the starting voltage, stopping if the current gets too high. Then fill in any
		#values which do not get recorded because of the cutoff. Next the voltage will ramp down toward
		#the ending voltage, again stopping once the current gets limited. Values which are cutoff are again
		#filled in and the program ramps up again until the cuttoff. The final values are filled into
		#the list and the data is stored in the array.
		
		#Keyword Arguments:
		#yloc -- the current y location
		#xloc -- the current x location
		#lim_min -- the minimum voltage/limit
		#lim_max -- the maximum voltage/limit
		
		#'''
		#self.IV[str((int(yloc),int(xloc)))] = []
		#nsteps = self.getNSteps(self.package.IV_MIN[0],self.package.IV_MAX[0],self.package.IV_STEPS[0])

		#gen_list = []
		#bias = copy(self.tip.convertToInteger(0))
		#maxval = self.tip.convertToInteger(self.package.IV_MAX[0])
		#while bias < maxval:
			#gen_list.append(bias)
			#bias += nsteps
		#gen_list.reverse()
		#mid_len = len(gen_list)-1
		#bias = copy(self.tip.convertToInteger(0))
		#while len(gen_list) < self.package.IV_STEPS[0]:
			#bias -= nsteps
			#gen_list.append(bias)
		
		#for i in range(self.package.IV_MULTIZ[0]+1):
			#xlist_f = []
			#ylist_f = []
			#xlist_r = []
			#ylist_r = []
			#self._holdTip()
			#self._moveZPlane(self.package.IV_Z[i][0])
			#self.tip.microdelay(self.package.IV_SETDELAY[0])
			#if self.tip.currentPhysical() > 0:
				#self._jumpTipInt(gen_list[mid_len])
				#self.tip.microdelay(self.package.IV_PREDELAY[0])
				#index = copy(mid_len)
				#self.tip.microdelay(self.package.IV_DELAY[0])
				#current = self.tip_current.readFew(self.package.IV_READS[0])

				#while current < lim_max and index > 0:
					#index -= 1
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
				#count = 0
				#while count < index:
					#xlist_f.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_f.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count += 1
				#while lim_min < current and index < len(gen_list)-1:
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#xlist_f.append(self._convertBias(self.tip.currentPhysical()))
					#ylist_f.append(current)
					#index += 1
				#count = len(xlist_f)-1
				#while len(xlist_f) < self.package.IV_STEPS[0]:
					#xlist_f.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_f.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count += 1
				#count = len(gen_list)-1
				#self.tip.microdelay(self.package.IV_MIDDELAY[0])
				#while count > index:
					#xlist_r.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_r.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count -= 1
				#while current < lim_max and index > 0:
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#xlist_r.append(self._convertBias(self.tip.currentPhysical()))
					#ylist_r.append(current)
					#index -= 1
				#count = copy(index)
				#while len(xlist_r) < self.package.IV_STEPS[0]:
					#xlist_r.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_r.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count -= 1
				
			#else:
				#gen_list.reverse()
				#index = len(gen_list)-1-copy(mid_len)
				#self._jumpTipInt(gen_list[index])
				#current = self.tip_current.readFew(self.package.IV_READS[0])
				
				#while lim_min < current and index > 0:
					#index -= 1
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
				#count = 0
				#while count < index:
					#xlist_f.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_f.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count += 1
				#while current < lim_max and index < len(gen_list)-1:
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#xlist_f.append(self._convertBias(self.tip.currentPhysical()))
					#ylist_f.append(current)
					#index += 1
				#count = len(xlist_f)-1
				#while len(xlist_f) < self.package.IV_STEPS[0]:
					#xlist_f.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_f.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count += 1
				#count = len(gen_list)-1
				#self.tip.microdelay(self.package.IV_MIDDELAY[0])
				#while count > index:
					#xlist_r.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_r.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count -= 1
				#while current > lim_min and index > 0:
					#self._jumpTipInt(gen_list[index])
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#xlist_r.append(self._convertBias(self.tip.currentPhysical()))
					#ylist_r.append(current)
					#index -= 1
				#count = copy(index)
				#while len(xlist_r) < self.package.IV_STEPS[0]:
					#xlist_r.append(self._convertBias(self.tip.convertToPhysical(gen_list[count])))
					#current = self.tip_current.readFew(self.package.IV_READS[0])
					#ylist_r.append(current)
					#self.tip.microdelay(self.package.IV_DELAY[0])
					#count += 1
			#xlist_r.reverse()
			#ylist_r.reverse()
			#self.IV[str((int(yloc),int(xloc)))].append([[xlist_f,ylist_f],[xlist_r,ylist_r]])
		#self._moveZPlane(0)
		#self._jumpTip(self._convertBias(self.package.SAMPLE_BIAS[0]))
		#self._releaseHold()
		#self.tip.microdelay(self.package.IV_POSTDELAY[0])
	
	
	#def _doIZ(self,yloc,xloc):
		#'''
		#Does an IZ at the given location.
		
		#Keyword Arguments:
		#yloc -- the current y pixel location
		#xloc -- the current x pixel location
		
		#'''

		#self.IZ[str((int(yloc),int(xloc)))] = []
		#zstart = self.angstromsToVolts(self.package.IZ_START[0],self.zplane.aoid)
		#zend = self.angstromsToVolts(self.package.IZ_END[0],self.zplane.aoid)
		#zstart*= (1./self.GAIN_ZPLANE)
		#zend*= (1./self.GAIN_ZPLANE)
		#if zend < zstart:
			#direction = -1
			#nsteps = self.getNSteps(zend,zstart,self.package.IZ_STEPS[0])
		#else:
			#direction = 1
			#nsteps = self.getNSteps(zstart,zend,self.package.IZ_STEPS[0])
		#period = (1./self.ZPLANE_RATE)*1e6
		#self._holdTip()
		#self._moveZPlane(self.package.IZ_START[0])
		#self.zplane.microdelay(self.package.IZ_SETDELAY[0])
		#xlist_f = []
		#ylist_f = []
		#xlist_r = []
		#ylist_r = []
		#for i in range(self.package.IZ_STEPS[0]):
			#xlist_f.append(self.zplane.currentPhysical())
			#ylist_f.append(self.tip_current.readFew(self.package.IZ_READS[0]))
			#self.zplane.writeSteps(nsteps,direction,delay=(period/4.))
			#self.zplane.microdelay(self.package.IZ_DELAY[0])
		#for i in range(self.package.IZ_STEPS[0]):
			#self.zplane.writeSteps(nsteps,-direction,delay=(period/4.))
			#self.zplane.microdelay(self.package.IZ_DELAY[0])
			#xlist_r.append(self.zplane.currentPhysical())
			#ylist_r.append(self.tip_current.readFew(self.package.IZ_READS[0]))
		#ylist_f = n.multiply(n.array(ylist_f),self.Z_MULTIPLIER).tolist()
		#ylist_r = n.multiply(n.array(ylist_r),self.Z_MULTIPLIER).tolist()
		#self.IZ[str((int(yloc),int(xloc)))].append([[xlist_f,ylist_f],[xlist_r,ylist_r]])
		#self._moveZPlane(0)
		#self._releaseHold()
		#self.zplane.microdelay(self.package.IZ_POSTDELAY[0])

	def _doIZ(self,yloc,xloc):
		'''
		Does an IZ at the given location.
		
		Keyword Arguments:
		yloc -- the current y pixel location
		xloc -- the current x pixel location
		
		'''

		self.IZ[str((int(yloc),int(xloc)))] = []
		self._outq.put('not implemented\n')
	
	#def _doZV(self,yloc,xloc): #convert to use rwScan
		#'''
		#Does an ZV at the given location.
		
		#Keyword Arguments:
		#yloc -- the current y pixel location
		#xloc -- the current x pixel location
		
		#'''
		#self.ZV[str((int(yloc),int(xloc)))] = []
		
		###calculate the voltage step size between points
		#dv = abs(self.getdV(self.package.ZV_END[0],self.package.ZV_START[0],self.package.ZV_STEPS[0]))
		#start = self.package.ZV_START[0] 

		#if self.package.ZV_START[0] > self.package.ZV_END[0]:
			#dv *= -1
		
		#xlist_f = []
		#ylist_f = []
		#xlist_r = []
		#ylist_r = []
		#self._setTip(start) #jump the tip to the starting voltage
		#self.tip.microdelay(self.package.ZV_PREDELAY[0])
		#for i in range(self.package.ZV_STEPS[0]):
			#xlist_f.append(self._convertBias(self.tip.currentPhysical()))
			#ylist_f.append(self.zpos.readFew(self.package.ZV_READS[0]))
			#self.ivTip2(dv) #change the bias by the amount dv
			#self.tip.microdelay(self.package.ZV_DELAY[0])
		#self.dOut.microdelay(self.package.ZV_MIDDELAY[0])
		#for i in range(self.package.ZV_STEPS[0]):
			#self.ivTip2(-dv)
			#self.tip.microdelay(self.package.ZV_DELAY[0])
			#xlist_r.append(self._convertBias(self.tip.currentPhysical()))
			#ylist_r.append(self.zpos.readFew(self.package.ZV_READS[0]))
		#ylist_f = n.multiply(n.array(ylist_f),self.Z_MULTIPLIER).tolist()
		#ylist_r = n.multiply(n.array(ylist_r),self.Z_MULTIPLIER).tolist()
		#xlist_r.reverse()
		#ylist_r.reverse()
		#self.ZV[str((int(yloc),int(xloc)))].append([[xlist_f,ylist_f],[xlist_r,ylist_r]])

		#self._jumpTip(self._convertBias(self.package.SAMPLE_BIAS[0])) #return the bias to the scan voltage
		#self.tip.microdelay(self.package.ZV_POSTDELAY[0])

	def _doZV(self,yloc,xloc): #convert to use rwScan
		'''
		Does an ZV at the given location.
		
		Keyword Arguments:
		yloc -- the current y pixel location
		xloc -- the current x pixel location
		
		'''
		self.ZV[str((int(yloc),int(xloc)))] = []
		
		self._outq.put('not implemented\n')
	
	def getNSteps(self,vmin,vmax,pts):
		'''
		Returns the number of steps per point.
		
		Keyword Arguments:
		vmin -- the voltage minimum
		vmax -- the voltage maximum
		pts -- the number of points taken between these voltages
		
		'''
		#if not vmin < vmax: direction = -1
		#else: direction = 1
		
		return int(round(((vmax - vmin)/float(pts))*(65536/20.)))
	
	def getdV(self,v1,v2,pts):
		'''
		Calculates the voltage step size for a range from v1 to v2 with
		a number of points pts.
		
		'''
		return float(v2-v1)/pts
	
	def _lineDelay(self):
		'''
		Executes the line delay defined in the package.
		
		'''
		time.sleep((self.package.LINE_DELAY[0]/1000.))
	
	def setSleepWave(self):
		'''
		Sets up the sleep time for each pixel point.
		
		'''
		sleep_wave = []
		nsteps = self.package.SCAN_X[0]*self.package.DAC_AVGS[0]
		sleep_wave = n.zeros(nsteps)
		if self.package.POINT_DELAY[0]: sleep_wave += self.package.POINT_DELAY[0]
		sleep_wave = sleep_wave.tolist()
		self.sleep_wave = sleep_wave
	
	#def _setBias(self):
		#pass

class LineOperations(PointOperations):
	'''Functions specific to line level operations'''
	scanNo = None
	def _advanceLine(self):
		'''
		Advances to the next line (or y location). Will move down for top
		down scans.
		
		'''
		nsteps = self.package.DAC_AVGS[0]*self.package.DAC_STEP[0]
		if self.package.TOP_DOWN[0]:
			self.ymove.writeSteps(-nsteps,1,delay=(self.XMAX_PERIOD/4.))
			self.yloc -= 1
		else:
			self.ymove.writeSteps(nsteps,1,delay=(self.XMAX_PERIOD/4.))
			self.yloc += 1
	
	#def _withdrawLine(self):
		#nsteps = self.package.DAC_AVGS[0]*self.package.DAC_STEP[0]
		#self.ymove.writeSteps(-nsteps,1)
		#self.yloc -= 1
	
	def _getSpecLoc(self):
		if not self._ivq.empty():
				self.SPEC_MASK = self._ivq.get(block=False)
	
	def _sendLine(self):
		'''
		Puts the line data into the display queue. Displays the 0th layer and
		the direction specified in worldsettings.
		
		'''
		self.lineclass.__setattr__('LINE',self.yloc)
		self.lineclass.__setattr__('data',self.Z[0][ws.DISPLAY_DIR][self.yloc:self.yloc+1,:])
		self._dataq.put(self.lineclass)
	
	def _sendSpec(self):
		'''
		Puts the line data into the display queue. Displays the 0th layer and
		the direction specified in worldsettings.
		
		'''
		if not self.scanNo: self.scanNo = self.acquireNumber()
		specdata = self.SPEC_MASK
		scantype = self.package.TYPE
		extent = [0,self.package.PHYS_X[0],0,self.package.PHYS_Y[0]]
		specclass = LineInfo(specdata,self.yloc,specdata.shape,self.scanNo,scantype,extent)
		specclass.SCANSTAMP = self.scanStamp
		specclass.DATATYPE = 'spec'
		#zout = self.package.GAIN_ZOUT[0]*10+1
		#specclass.__setattr__('ZSCALE',self.package.GAIN_ZHV[0]/zout)
		specclass.__setattr__('GRIDX',self.package.GRIDX[0])
		specclass.__setattr__('GRIDY',self.package.GRIDY[0])
		specclass.__setattr__('UO',self.package.UO)
		specclass.__setattr__('CHECKER',self.package.CHECKER[0])
		self._dataq.put(specclass)
	
	def _getNewParameters(self):
		'''
		Currently unused and probably not working.
		'''
		if not self._paramsq.empty():
			self._paramsq.get(block=False)
			#self.package = self._paramsq.get(block=False)
			pass

class ImageOperations(LineOperations):
	'''Functions specific to image level operations'''
	xloc = -1
	yloc = 0

	#HOLD = ws.HOLD
	#RETRACT = ws.RETRACT
	#RETRACT_NREADS = ws.RETRACT_NREADS
	#RETRACT_DELAY = ws.RETRACT_DELAY
	SWEEP_NREADS = ws.SWEEP_NREADS
	SWEEP_DELAY = ws.SWEEP_DELAY
	Z_MAX_ERR = ws.Z_MAX_ERR
	SWEEP_TIMEOUT = ws.SWEEP_TIMEOUT
	
	XOFF_RATE = ws.XOFF_RATE
	YOFF_RATE = ws.YOFF_RATE
	#ZOFF_RATE = ws.ZOFF_RATE
	XSTART_RATE = ws.XSTART_RATE
	YSTART_RATE = ws.YSTART_RATE
	XRETURN_RATE = ws.XRETURN_RATE
	YRETURN_RATE = ws.YRETURN_RATE
	XMAX_RATE = ws.XMAX_RATE
	YMAX_RATE = ws.YMAX_RATE
	XMAX_PERIOD = (1./XMAX_RATE)*1e6
	YMAX_PERIOD = (1./YMAX_RATE)*1e6
	ZPLANE_RATE = ws.ZPLANE_RATE
	#GAIN_ZPLANE = ws.GAIN_ZPLANE
	OFFSET_STEP = ws.OFFSET_STEP
	TIP_RATE = ws.TIP_RATE
	TIP_PERIOD = (1./TIP_RATE)*1e6
	TIP_STEP = ws.TIP_STEP
	
	VOLTS_PER_ADU = ws.VOLTS_PER_ADU
	ANG_PER_VOLT_X = ws.ANG_PER_VOLT_X
	ANG_PER_VOLT_Y = ws.ANG_PER_VOLT_Y
	SCAN_GAIN = ws.SCAN_GAIN
	spec_scans = ['imagewiv','imagewiz','imagewzv']
	
	def __init__(self,package,queues,analog_io,digital_io):
		for key in queues.keys(): self.__setattr__(key,queues[key])
		for key in analog_io.keys(): self.__setattr__(key,analog_io[key])
		for key in digital_io.keys(): self.__setattr__(key,digital_io[key])
		#self.cM = CoarseMove(self._inq,self._outq,self.zpos,self.pport,self.dOut)
		self.package = package
		self.xvolts = 0
		self.yvolts = 0
		self.scanStamp = int(time.time())
		#Create the arrays and dictionaries for storage
		if hasattr(self.package,'SCAN_Y'):
			self.checkXRate()
			self.checkYRate()
			self.SPEC_MASK = n.zeros((self.package.SCAN_Y[0],self.package.SCAN_X[0]))
			self.Z = []
			temp_Z = n.zeros((self.package.SCAN_Y[0],self.package.SCAN_X[0]))
			if hasattr(self.package,'N_BIAS_STEPS'):
				for i in range(self.package.N_BIAS_STEPS[0]+1):
					self.Z.append([copy(temp_Z),copy(temp_Z)])
			else:
				self.Z.append([copy(temp_Z),copy(temp_Z)])
			self.physicalImage()
			self.makeLineclass()
		else:
			self.Z = n.zeros((1,1))
			self.SPEC_MASK = n.zeros((1,1))
		if hasattr(self.package,'POINT_DELAY'):
			self.setSleepWave()
		self.IV = {}
		self.IZ = {}
		self.ZV = {}
	
	def _initParams(self):
		while 1:
			try: 
				self.package = self._paramsq.get(block=True)
				try: 
					#print self.package.SCAN_Y[0]
					#print self.package.SCAN_X[0]
					break
				except:pass
			except: time.sleep(0.01)
	
	def makeLineclass(self):
		if not self.scanNo: self.scanNo = self.acquireNumber()
		scantype = self.package.TYPE
		extent = [0,self.package.PHYS_X[0],0,self.package.PHYS_Y[0]]
		self.lineclass = LineInfo(None,0,self.Z[0][0].shape,self.scanNo,scantype,extent)
		self.lineclass.SCANSTAMP = self.scanStamp
		self.lineclass.__setattr__('UO',self.package.UO)
		#zout = self.package.GAIN_ZOUT[0]*10+1
		#self.lineclass.__setattr__('ZSCALE',copy(self.package.GAIN_ZHV[0]/zout))
		#self.lineclass.__setattr__('ZOUT',copy(self.package.GAIN_ZOUT[0]))
		#self.lineclass.__setattr__('ZHV',copy(self.package.GAIN_ZHV[0]))
		if scantype in self.spec_scans:
			self.lineclass.__setattr__('GRIDX',self.package.GRIDX[0])
			self.lineclass.__setattr__('GRIDY',self.package.GRIDY[0])
			self.lineclass.__setattr__('CHECKER',self.package.CHECKER[0])
	
	def checkXRate(self):
		'''
		Checks the max rate from the client and makes sure that it
		is not greater than the global max rate defined in the world settings.
		
		'''
		xhv_gain = self.package.GAIN_XHV[0]
		max_xrate = self.package.MAX_RATE[0]/(self.VOLTS_PER_ADU*self.ANG_PER_VOLT_X*self.SCAN_GAIN*xhv_gain)
		if not max_xrate < self.XMAX_RATE:
			self.package.MAX_RATE[0] = self.XMAX_RATE*self.VOLTS_PER_ADU*self.ANG_PER_VOLT_X*self.SCAN_GAIN*xhv_gain
		else:
			self.XMAX_RATE = max_xrate
		self.XMAX_PERIOD = (1./self.XMAX_RATE)*1e6

	
	def checkYRate(self):
		'''
		Checks the max rate from the client and makes sure that it
		is not greater than the global max rate defined in the world settings.
		
		'''
		yhv_gain = self.package.GAIN_YHV[0]
		max_yrate = self.package.MAX_RATE[0]/(self.VOLTS_PER_ADU*self.ANG_PER_VOLT_Y*self.SCAN_GAIN*yhv_gain)
		if not max_yrate < self.YMAX_RATE:
			self.package.MAX_RATE[0] = self.YMAX_RATE*self.VOLTS_PER_ADU*self.ANG_PER_VOLT_Y*self.SCAN_GAIN*yhv_gain
		else:
			self.YMAX_RATE = max_yrate
		self.YMAX_PERIOD = (1./self.YMAX_RATE)*1e6
	
	def _approachSample(self):
		'''
		Starts the approach. Approach() is defined in an external module, so the hardware
		and queues must be passed to the package.
		
		'''
		cent = self.package.CENTER[0]
		cut = self.package.CUTOFF[0]
		app = Approach(self._inq,self._outq,self.servo,self.pport,cent,cut) #! Update this in Approach
		app._Approach()

	def _checkQueue(self):
		'''
		Checks the inq for any messages from the stm.
		
		'''
		if len(self._inq.queue) > 0: return self._inq.get()
		else:
			time.sleep(0.01)
			return None
	
	#def _coarseForward(self,steps,rate): self.cM.coarseForward(steps,rate)
	#def _coarseBack(self,steps,rate):  self.cM.coarseBack(steps,rate)
	#def _coarseLeft(self,steps,rate):  self.cM.coarseLeft(steps,rate)
	#def _coarseRight(self,steps,rate):  self.cM.coarseRight(steps,rate)
	#def _coarseUp(self,steps,rate):  self.cM.coarseUp(steps,rate)
	#def _coarseDown(self,steps,rate):  self.cM.coarseDown(steps,rate)
	
	#def _manualApproach(self):
		#approaching = True
		#while approaching:
			#val = self._checkQueue()
			#if val == 'abort': approaching = False
			#elif val != None:
				#direc = copy(val[0])
				#rate = int(copy(val[1:4]))
				#val = None
				#loop='go'
				#if direc == ws.CM_FORWARD:
					#direcstr = 'Forward'
					#def coarseMove(steps,rate): self._coarseForward(steps,rate)
				#elif direc == ws.CM_BACK:
					#direcstr = 'Back'
					#def coarseMove(steps,rate): self._coarseBack(steps,rate)
				#elif direc == ws.CM_LEFT:
					#direcstr = 'Left'
					#def coarseMove(steps,rate): self._coarseLeft(steps,rate)
				#elif direc == ws.CM_RIGHT:
					#direcstr = 'Right'
					#def coarseMove(steps,rate): self._coarseRight(steps,rate)
				#elif direc == ws.CM_UP:
					#direcstr = 'Up'
					#def coarseMove(steps,rate): self._coarseUp(steps,rate)
				#elif direc == ws.CM_DOWN:
					#direcstr = 'Down'
					#def coarseMove(steps,rate): self._coarseDown(steps,rate)
				#msg = 'Coarse %s Motion Initiated, rate = %d'%(direcstr,rate)
				#self._outq.put(msg)
				#while loop != 'stop':
					#coarseMove(1,rate)
					#loop = self._checkQueue()
				#self._outq.put('Coarse Motion Halted.')

	def _convertBias(self,bias,tosample=True):
		'''
		Converts tip bias to sample bias or sample to tip.
		
		'''
		return -bias
	
	def _retractTip(self,vlts=9.5,wait=0.5):
		self._outq.put('Retracting Tip')
		self.servo.Retract()
		self._sendState(wait=wait)
	
	def _sweepTip(self,wait=0.5):
		self._outq.put('Sweeping Tip')
		self.servo.UnRetract()
		self._sendState(wait=wait)
	
	def _holdTip(self,wait=0.5):
		'''
		Sets the z position hold on the servo.
		
		'''
		self.servo.Hold()
		self._sendState(wait=wait)
	
	def _releaseHold(self,wait=0.5):
		'''
		Releases the z position hold on the servo.
		
		'''
		
		self.servo.UnHold()
		self._sendState(wait=wait)
	
	def _setActiveDAC(self,dac,wait=0.5):
		'''
		Sets the active dac (high/low gain)
		'''
		self.servo.SetActiveDAC(dac)
		time.sleep(0.25) # a settle time to wait for dacs to change
		self._sendState(wait=wait)

	def _swapVoltage(self,wait=0.5):
		'''
		Calls the command to swap the voltage from the
		low gain into the high gain.
		'''
		self.servo.SwapDACVolts()
		time.sleep(1.0) # wait for zeroing
		self._sendState(wait=wait)

	def _stepResponseOn(self):
		'''
		Enables step response mode on the servo.
		'''
		self.servo.StepResponse()
	
	def _stepResponseOff(self):
		'''
		Disables step response mode on the servo.
		'''
		self.servo.UnStepResponse()	
		
	def _checkHardware(self):
		print 'Hardware ready'
	
	def _returnTip(self):
		'''
		Sweeps the tip into the specified error voltage
		and checks for tunneling.
		
		Keyword Arguments:
		volts -- the max sweep voltage
		
		'''
		
		errlist = []
		self._outq.put('Sweeping Tip')
		self.servo.UnRetract()
		start_time = time.time()
		err = self.servo.ZHeight()
		errlist.append(err)
		time.sleep(self.SWEEP_DELAY)
		while 1:
			err = self.servo.ZHeight()
			errlist.append(err)
			time.sleep(self.SWEEP_DELAY)
			if err > self.Z_MAX_ERR:
				break
			elif err < self.Z_MAX_ERR and time.time()- start_time > self.SWEEP_TIMEOUT: break
			else:
				pass
		return err
	
	def _getZPos(self):
		'''
		returns the current z position.
		'''
		time.sleep(0.5)
		return self.servo.ZHeight()
	
	def _getDACPositions(self):
		'''
		Gets the DAC Positions from the servo.

		'''
		dacs = self.servo.DACUpdate()
		state = self.servo.GetState()
		self._outq.put('DACUP,%0.2f,%0.2f,%d'%(dacs[0],dacs[1],state))

	def _sendState(self,wait=0.5):
		if wait == 0: pass
		else: time.sleep(wait)
		state = self.servo.GetState()
		self._outq.put('STATE,%d'%state)

	def _autoZ(self,autovolts):
		'''
		Auto zeros the tip by checking the error voltage
		and setting the z offset.
		
		'''
		self._outq.put('Auto-Zeroing Not Implemented')
	
	def _SetKP(self,kp):
		'''
		Calls the function to set the proportional gain.

		'''
		self.servo.SetPGain(kp)
		self._outq.put('Kp Set To: %0.6f'%kp)

	def _SetKI(self,ki):
		'''
		Calls the function to set the proportional gain.

		'''
		self.servo.SetIGain(ki)
		self._outq.put('Ki Set To: %0.6f'%ki)

	def _SetKD(self,kd):
		'''
		Calls the function to set the proportional gain.

		'''
		self.servo.SetDGain(kd)
		self._outq.put('Kd Set To: %0.6f'%kd)

	#def _setTip(self,bias,report=False,restrict=True):
		#'''
		#Sets the tip bias to the value given in volts.
		
		#'''
		#vlts = self.servo.TIPVOLTS
		#if restrict: # check if bias is above the minimum
			#if abs(bias) < ws.BIAS_RESTRICT:
				#bias = cmp(bias,0)*ws.BIAS_RESTRICT
		#self.servo.TipVolts(bias)
		#if report: self._outq.put('Tip bias set to %s'%(self.tip.currentPhysical()))
		#return bias
	
	def _jumpTip(self,bias,report=False,rate=None,restrict=True):
		'''
		Jumps the tip to the given voltage.
		
		'''
		if not rate: rate = self.TIP_RATE
		if restrict: # check if bias is above the minimum
			if abs(bias) < ws.BIAS_RESTRICT:
				bias = cmp(bias,0)*ws.BIAS_RESTRICT
		self.servo.TipVolts(bias,rate=rate)
		if report: self._outq.put('Tip bias set to %0.2f'%(self.servo.TIPVOLTS))
		#self.tip.microdelay(self.TIP_PERIOD/4.)
	
	def _setCurrent(self,curr,report=True):
		self.servo.CurrentSetpoint(curr)
		time.sleep(0.25) # wait for servo to equilibrate
		if report: self._outq.put('Tunneling current set to %0.2f'%(self.servo.ISET))

	#def _jumpTipInt(self,intbias):
		#'''
		#Jumps the tip to the given voltage.
		
		#'''
		#self.tip.writeSingle(intbias)
		##self._outq.put('Tip bias set to %s'%(self.tip.currentPhysical()))
		#self.tip.microdelay(self.TIP_PERIOD/4.)
	
	def calcPeriod(self,rate):
		'''
		Calculates the period from a rate.
		
		'''
		if rate: return (1./rate)*1000000  #in us
		else: return 0
	
	def _returnY(self):
		'''
		Moves the y position to the zero position.
		
		'''
		self._outq.put('Moving Y Piezo back to Zero Position')
		self.ymove.rampVolts(0,1,rate=self.YRETURN_RATE)
	
	def _returnX(self):
		'''
		Moves the x position to the zero position.
		
		'''
		self._outq.put('Moving X Piezo back to Zero Position')
		self.xmove_zpos.rampVolts(0,1,rate=self.XRETURN_RATE)
	
	def _moveXOffset(self,xang):
		'''
		Moves the piezos to the offset voltages using a linear 100-point ramp.
		
		Keyword Arguments:
		xvolt -- the offset x voltage
		
		'''
		xvolt = self.angstromsToVolts(xang,self.xoff.aoid)
		self._outq.put('Setting X Offset to %s volts'%str(xvolt)[0:5])
		self.xoff.rampVolts(xvolt,self.OFFSET_STEP,rate=self.XOFF_RATE)
	
	def _moveYOffset(self,yang):
		'''
		Moves the piezos to the offset voltages using a linear 100-point ramp.
		
		Keyword Arguments:
		yvolt -- the offset y voltage
		
		'''
		yvolt = self.angstromsToVolts(yang,self.yoff.aoid)
		self._outq.put('Setting Y Offset to %s volts'%str(yvolt)[0:5])
		self.yoff.rampVolts(yvolt,self.OFFSET_STEP,rate=self.YOFF_RATE)
	
	def _checkMessages(self,cmd,clearq=False):
		'''
		Checks the inq for any messages from the stm.
		
		'''

		if cmd in self._inq.queue:
			#while cmd in self._inq.queue:
				#self._inq.queue.remove(cmd)
			if cmd == 'abort': self._outq.put('Aborting Scan..')
			if clearq: self._inq.queue.clear()
			return True
		else:
			if clearq: self._inq.queue.clear()
			return False
	
	def _setYStart(self):
		'''
		Finds the y starting position and moves the piezo there.
		
		'''
		dac_step = self.package.DAC_STEP[0]
		dac_avgs = self.package.DAC_AVGS[0]
		maxdata = self.ymove.device.AO_MAXDATA
		scan_y = self.package.SCAN_Y[0]
		
		n_steps = dac_avgs*scan_y
		self._outq.put('Moving Y Piezo to Starting Position')
		move = int(n_steps*dac_step/2)
		per = self.calcPeriod(ws.YSTART_RATE)
		if move > ((maxdata+1)/2): move = ((maxdata+1)/2)
		if self.package.TOP_DOWN[0]:
			self.yloc = self.package.SCAN_Y[0]-1
			self.ymove.writeSteps(move,1,delay=(per/4.))
		else: self.ymove.writeSteps(-move,1,delay=(per/4.))
	
	
	def _setXStart(self):
		'''
		Finds the x starting position and moves the piezo there.
		
		'''
		dac_step = self.package.DAC_STEP[0]
		dac_avgs = self.package.DAC_AVGS[0]
		maxdata = self.xmove_zpos.device.AO_MAXDATA
		scan_x = self.package.SCAN_X[0]
		
		n_steps = dac_avgs*scan_x
		self._outq.put('Moving X Piezo to Starting Position')
		move = int(n_steps*dac_step/2)
		per = self.calcPeriod(ws.XSTART_RATE)
		if move > ((maxdata+1)/2): move = ((maxdata+1)/2)
		self.xmove_zpos.writeSteps(-move,1,delay=(per/4.))
	
	def _initSpectroscopy(self):
		'''
		Sets up the iv mask from the grid settings and calculates the step size.
		
		'''
		if self.package.TYPE in self.spec_scans:
			startx = int((self.package.STARTX[0]/100.) * self.package.SCAN_X[0])
			endx = int((self.package.ENDX[0]/100.) * self.package.SCAN_X[0])
			starty = int((self.package.STARTY[0]/100.) * self.package.SCAN_Y[0])
			endy = int((self.package.ENDY[0]/100.) * self.package.SCAN_Y[0])
			if startx == endx: endx = startx+1
			if starty == endy: endy = starty+1
			self.writeRect(self.SPEC_MASK,startx,starty,endx,endy)
		if self.package.TYPE[0:5] == 'image': self._sendSpec()
		
	def _initLinecut(self):
		'''
		Sets up the iv mask from the grid settings and calculates the step size.
		
		'''
		if self.package.TYPE in self.spec_scans:
			if self.package.AXIS[0] == 0: #0 is for a horizontal linecut, 1 is for a vertical linecut
				startx = self.package.START[0]
				self.writeLine(startx)
			else:
				starty = self.package.START[0]
				self.writeLine(starty)
			self.writeLine(self.SPEC_MASK,startx)
		if self.package.TYPE[0:5] == 'image': self._sendSpec()
	
	def _scanDelay(self):
		'''
		A sleep delay before the start of the scan.
		
		'''
		self._outq.put('Delaying Before Scan')
		time.sleep(self.package.SCAN_DELAY[0]/1000.)
	
	def sortCoord(self,x0,y0,x1,y1):
		list1 = [x0,x1]
		list2 = [y0,y1]
		list1.sort()
		list2.sort()
		return list1[0],list2[0],list1[1],list2[1]
	
	def writeRect(self,A,x0,y0,x1,y1):
		(xx,yx,xy,yy) = self.sortCoord(x0,y0,x1,y1)
		gridx = self.package.GRIDX[0]
		gridy = self.package.GRIDY[0]
		if self.package.CHECKER[0]:
			A[yx:yy,xx:xy][::2*gridy,::gridx] = 1
			A[yx:yy,xx:xy][gridy::2*gridy,int(gridx/2.)::gridx] = 1
		else:
			A[yx:yy,xx:xy][::gridy,::gridx] = 1
		self.SPEC_MASK = A
		
	def writeLine(self,A,pixel):
		grid = self.package.GRID[0]
		axis = self.package.AXIS[0]
		if axis == 0: #0 is horizontal
			A[pixel,::grid] = 1
		else:
			A[::grid,pixel] = 1
		self.SPEC_MASK = A
	
	def _blastCrap(self):
		'''
		Oscialltes the tip voltage at the given frequency to blast the crap
		off of it!
		
		'''
		freq = self.package.CB_FREQ[0]
		vmax = self.package.CB_MAX[0]
		vmin = self.package.CB_MIN[0]
		sbias = self.package.SAMPLE_BIAS[0]
		
		if freq > self.TIP_RATE:
			freq = self.TIP_RATE
		
		while 1:
			self._jumpTip(vmax,rate=freq)
			self._jumpTip(vmin,rate=freq)
			ret = self._checkMessages('abort',clearq=True)
			if ret: break
			
		self._jumpTip(self._convertBias(sbias))
	
	def _rsyncData(self):
		'''
		Rsyncs the data to the specified server.
		'''
		self._outq.put('Syncing data to server '+self.package.STORAGE_SERVER[0])
		localpath = ws.BASE_DIRECTORY+ws.DATA_DIRECTORY+'/'
		remotepath = self.package.SSH_USER[0]+'@'+self.package.STORAGE_SERVER[0]+':'+self.package.REMOTE_PATH[0]+'/'
		res = pexpect.spawn('rsync -'+self.package.RSYNC_FLAGS[0]+' '+localpath+' '+remotepath)
		try: res.expect(pexpect.EOF,timeout=3)
		except:
			while 1:
				mesg = res.before
				self._outq.put(mesg)
				if 'password' in mesg or 'RSA' in mesg:
					reply = self._inq.get()
					res.sendline(reply)
				try:
					res.expect(pexpect.EOF,timeout=3)
					break
				except: pass
		self._outq.put(res.before)
		self._outq.put('Transfer complete. Closing connection to server.')
		res.close()
		

		
	
	def _addComments(self):
		'''
		Allows the user to add comments to the file before
		saving.
		
		'''
		self._outq.put('Would you like to attach comments? (y/n)')
		while 1:
			res = self._inq.get()
			if res == 'y' or res == 'yes':
				res = True
				break
			elif res == 'n' or res == 'no':
				res = False
				break
			else: pass
		if res:
			self._outq.put('Send comments to attach.')
			com = self._inq.get()
			self.package.COMMENTS = com
		else: self.package.COMMENTS = 'No Comment.'
	
	def _saveData(self,temp=False,bypass=False,abort=False):
		'''
		Saves the data to file.
		
		'''
		if not bypass and not abort:
			if self.package.SAVE_SCAN == 'n': pass
			elif self.package.SAVE_SCAN == 'a':
				self._outq.put('Would you like to save the scan? (y/n)')
				while 1:
					res = self._inq.get()
					if res == 'y' or res == 'yes':
						res = True
						break
					elif res == 'n' or res == 'no':
						res = False
						break
					else: pass
				if res:
					self._addComments()
					self._outq.put('Saving data to file.')
					sd.Class2NetCDF(self.package)
				else: self._outq.put('Continuing without saving.')
			elif self.package.SAVE_SCAN == 'f':
				self._outq.put('Saving data to file.')
				sd.Class2NetCDF(self.package)
			else:
				self._addComments()
				self._outq.put('Saving data to file.')
				sd.Class2NetCDF(self.package)
		elif bypass and not abort:
			self._outq.put('Saving data to file.')
			sd.Class2NetCDF(self.package)
		else:
			self._outq.put('Would you like to save the scan? (y/n)')
			while 1:
				res = self._inq.get()
				if res == 'y' or res == 'yes':
					res = True
					break
				elif res == 'n' or res == 'no':
					res = False
					break
				else: pass
			if res:
				self._addComments()
				self._outq.put('Saving data to file.')
				sd.Class2NetCDF(self.package)
	
	def acquireNumber(self):
		'''
		Retrieves a number from the number file CurrentScan and
		updates the file to the new current scan number.
		
		'''
		fh = open('CurrentScan','r')
		ret = fh.read()
		fh.close()
		return ws.STM_PREFIX+ret
	
	def _appendData(self):
		'''
		Appends the data to the package prior to saving/sending
		etc.
		
		'''
		self.package.IMAGEDATA = self.Z
		self.package.IV = self.IV
		self.package.SPEC_MASK = self.SPEC_MASK
		self.package.IZ = self.IZ
		self.package.ZV = self.ZV
		self.package.SCAN_NUMBER = self.acquireNumber()
		self.package.SCANSTAMP = self.scanStamp
		self.realPhys()
		self.package._complete()
	
	def realPhys(self):
		'''
		Retrieves the physical values from the board to account for
		any millivolt discrepancies.
		
		'''
		self.package.XOFF[0] = self.xoff.currentPhysical()
		self.package.YOFF[0] = self.yoff.currentPhysical()
		self.package.SAMPLE_BIAS[0] = self._convertBias(self.servo.TIPVOLTS)
	
	def _writeSettings(self):
		'''
		Writes current settings to disk. In case of crash,
		these settings can be loaded so the controller will pick
		up where it left off.
		'''
		rcv = Recovery(self.xoff.current_dac,self.yoff.current_dac,self.servo.TIPVOLTS)
		fh = open('crash.rcvry','w')
		cPickle.dump(rcv,fh)
		fh.close()
		self._outq.put('Saving crash recovery parameters.')
		
	def _restoreSettings(self):
		'''
		Recovers the crash parameters from the stored file and restores the current
		dac positions in the driver. Then the parameters are sent to the client to
		reset the values in the gui.
		
		'''
		fh = open('crash.rcvry','r')
		rcv = cPickle.load(fh)
		fh.close()
		self._outq.put('Restoring pre-crash parameters.')
		self.xoff.current_dac = rcv.xoff
		self.yoff.current_dac = rcv.yoff
		self.servo.TIPVOLTS = rcv.tip
		xangs = self.voltsToAngstroms(self.xoff.currentPhysical(),self.xoff.aoid)
		yangs = self.voltsToAngstroms(self.yoff.currentPhysical(),self.yoff.aoid)
		sample_bias = self._convertBias(self.servo.TIPVOLTS)
		
		self.package.XOFF[0] = xangs
		self.package.YOFF[0] = yangs
		self.package.SAMPLE_BIAS[0] = sample_bias
		self.package.ISET[0] = ws.DEFAULT_ISET
		
		self._jumpTip(self.servo.TIPVOLTS,report=True)
		self._moveXOffset(xangs)
		self._moveYOffset(yangs)
		
		
	
	def _sendFinal(self):
		'''
		Sends the final data back through the socket
		to the client.
		
		'''
		self._dataq.put(self.package)
